/** @file         demo.c
 *  @brief        dm_manager 使用例程
 *  @details      介绍如何使用 dm 来申请，释放，操作内存
 *  @author       Schips
 *  @date         2020/08/25
 *  @version      v1.0
 *  @copyright    Copyright By Schips, All Rights Reserved
 *
 ***********************************************************************************
 *
 *  @par 修改日志:
 *  <table>
 *  <tr><th>Date        <th>Version  <th>Author    <th>Description
 *  <tr><td>2020/08/25  <td>1.0      <td>Schips	   <td>创建初始版本
 *  </table>
 *
 **********************************************************************************
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/printk.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>

#include <linux/io.h>
#include <linux/uaccess.h>

#include <linux/miscdevice.h> 

#include <linux/interrupt.h>
#include "dm_mamanger.h"

/* 有关宏定义 */
#define DVC_NAME        "dm"  // 设备节点名称
#define DEV_COUNT       1       // 设备个数

#define DEVID_COUNT     1       // 设备号个数
#define DEV_MINOR       0   

/* 物理地址相关 */
// 寄存器地址
#define XPAR_M_AVALON_0_BASEADDR 0x43C00000
#define XPAR_M_AVALON_0_HIGHADDR 0x43C0FFFF


#define AXI_AVALON_REG_BASE  XPAR_M_AVALON_0_BASEADDR

#define AXI_AVALON_REG_ADDR_SIZE    XPAR_M_AVALON_0_HIGHADDR - XPAR_M_AVALON_0_BASEADDR
#define AXI_GPIO_LED_ADDR_SIZE      1
#define AXI_GPIO_BTN_ADDR_SIZE      1

// 物理地址指针
static void __iomem *   axi_avalon_base_va = NULL;

/* 前导声明函数*/
static int      dm_demo_open(struct inode *inode_p, struct file *file_p);
static ssize_t  dm_demo_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p);
static ssize_t  dm_demo_read(struct file *file_p, char __user *buf, size_t len, loff_t *loff_t_p);

void mem_cleanup(void);

/* 驱动有关结构体 */
dev_t           devid;          // 设备号
struct cdev     cdev = {        // 字符设备
    .owner  = THIS_MODULE,
};
struct class  * class;          // 类
struct device * device;         // 设备节点


static struct file_operations dm_demo_fops = {
    .owner  = THIS_MODULE,
    .open   = dm_demo_open,
    .read   = dm_demo_read,
    .write  = dm_demo_write,
};

static int __init dm_demo_init(void)
{
    int ret;
/* 模块框架 */
    // 注册设备号
    ret = alloc_chrdev_region(&devid, DEV_MINOR, DEVID_COUNT, DVC_NAME);
    if(ret < 0) {
        printk(KERN_WARNING "can not get device number\n");
        goto chrdev_region_error;
    }

    // 绑定 文件操作 集
    cdev_init(&cdev, &dm_demo_fops);

    // 注册字符设备
    ret = cdev_add(&cdev, devid, DEV_COUNT);
    if(ret < 0) {
        printk(KERN_WARNING "cdev_add falied\n");
        goto cdev_add_error;
    }

    // 创建类
    class = class_create(THIS_MODULE, DVC_NAME);
    if(IS_ERR(class))
    {
        printk(KERN_WARNING "class_create error\n");
        ret = -EBUSY;
        goto class_create_error;
    }
    // 创建设备节点
    device = device_create(class, NULL,
                          devid, NULL,
                          DVC_NAME);
    if(device == NULL) {
        ret = -EBUSY;
        printk(KERN_WARNING "device_create error\n");
        goto device_create_error;
    }

#if 1  /* 映射物理内存 */
     
    // 申请物理内存
    // 1、Avalon 通用寄存器区域
    axi_avalon_base_va = registe_mem(AXI_AVALON_REG_BASE, AXI_AVALON_REG_ADDR_SIZE, "axi_avalon");
    if(!axi_avalon_base_va){
        ret = -EBUSY;
        goto ioremap_error;
    }
#endif
	
	printk("<4>"" dm_demo_inited\n");
	return 0;
    /* 出错判断 */
ioremap_error :
    printk(KERN_WARNING "ioremap error\n");
    mem_cleanup();
//request_mem_region_error :
    device_destroy(class, devid);
    device = NULL;
device_create_error :
    class_destroy(class);
    class = NULL;
class_create_error :
    cdev_del(&cdev);
cdev_add_error :
    unregister_chrdev_region(devid, DEV_COUNT);
chrdev_region_error :
    return ret;
}

void mem_cleanup(void)
{
    dm_manager_deinit();

    axi_avalon_base_va     = NULL;
}

static int dm_demo_open(struct inode *inode_p, struct file *file_p)
{
    return 0;
}

static ssize_t  dm_demo_read(struct file *file_p, char __user *buf, size_t len, loff_t *loff_t_p)
{
    int ret;
    char read_buff[5] = {"ABC"};
    ret = copy_to_user(buf, read_buff, sizeof(read_buff));
	printk("<4>""dm_demo_read\n");
    return 0;
}

static ssize_t dm_demo_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p)
{
    int ret;
    char write_buff[5] = {0};
    char data;

	printk("<4>""dm_demo_write\n");

    ret = copy_from_user(write_buff, buf, len);

    if(0 != ret)
    {
	    printk("<4>""cp failed ret = %d\n", ret);
        return -1;
    }

    if(1 > len) 
    {
	    printk("<4>""cp failed, len = %d\n", len);
        return -1;
    }

	printk("<4>""cp len = %d\n", len);
    /* 用法1 ，直接操作申请的指针 */
    if(write_buff[0] == '1')
        writel(1, axi_avalon_base_va);
    else
        writel(0, axi_avalon_base_va);

    /* 用法2, 使用 mem_op_r, mem_op_w 2个接口*/
    if(write_buff[0] == '1')
        data = 1;
    else
        data = 1;

    mem_op_w("axi_avalon", 0, &data, 1);
    

    return 0;
}

static void __exit dm_demo_exit(void)
{
    // 注销 字符设备
    cdev_del(&cdev);

    // 归还设备号
    unregister_chrdev_region(devid, DEV_COUNT);

    // 删除设备节点
    device_destroy(class, devid);

    // 删除类
    class_destroy(class);

    // 卸载释放物理内存
    mem_cleanup();

	printk("<4>"" dm_demo_exit\n");
}

module_init(dm_demo_init);
module_exit(dm_demo_exit);

MODULE_AUTHOR("Schips");
MODULE_DESCRIPTION("DM Demo Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("V1.0");
